<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <div id="app">
        <p>{{obj.x}}---{{obj.y}}</p>
        <p>
            <button @click="addX">x++</button>
            <button @click="obj.y++">y++</button>
            <button @click="reset">reset</button>
        </p>
        <hr>
        <p>{{zhang.name}}---{{zhang.age}}</p>
        <p>{{zhang.hobby}}</p>
        <p>{{zhang.grade}}</p>
        <p>
            <button @click="changeAge">changeAge</button>
            <button @click="changeChinese">changeChinese</button>
        </p>
        <hr>
        <h2>计算属性</h2>
        <p>{{name}}---{{age}}</p>
        <p>
            <button @click="name='李四'">修改name</button>
            <button @click="age++">修改age</button>
        </p>
    </div>
</body>
<script type="module">
    // 整体载入  => 查看模块内暴露的变量和方法
    import * as Vue from "../js/vue.esm-browser.js";
    console.log(Vue);

    // 按需引入
    import { createApp, reactive, computed} from "../js/vue.esm-browser.js";


    // 组合式写法 可以理解为是选项式语法的优化, 但是最终还是会被解析成选项式语法
    // 在 setup() 函数中返回的对象会暴露给模板和组件实例。其他的选项也可以通过组件实例来获取 setup() 暴露的属性
    let app = createApp({
        setup() {

            /* 
                reactive()  创建一个对 复杂类型(数组 对象) 的引用  => 返回的是Proxy代理对象(Vue处理版)

                    1.  reactive()   返回的是Proxy代理对象,可以直接使用数据
                    2.  reactive 对数据的检测也是深层的


                    new Proxy(zhang,{
                        get(target,key){
                            track()  // 订阅操作  => 所有依赖当前数据的其他数据, 都是当前数据的订阅者
                            return target[key];
                        },
                        set(target,key,val){
                            target[key] = val;
                            trigger();  // 当数据被修改之后 更新视图,触发副作用操作(如果有其他数据依赖(订阅)当前数据,触发对应的副作用操作)
                        }
                    })

                缺点:
                    1. reactive() 一般接收复杂数据类型,不支持 string number boolean null undefined
                    2. 不能替换整个对象    => 所谓替换就是给变量赋新值, 原本引用的Proxy代理就被释放
                    3. 对解构操作不友好(丧失响应性)  当我们将响应式对象的原始类型属性解构为本地变量时，或者将该属性传递给函数时，我们将丢失响应性连接：
                        解构之后得到的是proxy代理对象中存储的值   


                computed()   创建计算属性的方法
                    接受一个 getter 函数，返回一个只读的响应式 ref 对象。该 ref 通过 .value 暴露 getter 函数的返回值。它也可以接受一个带有 get 和 set 函数的对象来创建一个可读,可写的 ref 对象。
                    1. 计算属性默认是只读的
            
            */

            const obj = reactive({ x: 1, y: 2 });
            const zhang = reactive({
                name: "张三",
                age: 18,
                hobby: ["唱", "跳", "rap"],
                grade: {
                    chinese: 55,
                    math: 66,
                    english: 77,
                }
            })

            // console.log(obj);
            // console.log(zhang);

            // 丧失响应性
            // const name = zhang.name;  // 对proxy取值 => 返回对应的值  "张三"
            // const age = zhang.age; // 对proxy取值 => 返回对应的值 18
            // console.log(name,age);


            // 保持响应性 => 可以通过计算属性(计算属性默认只读 => 只有取值,没有赋值)
            /* 
                const nameRef = {
                    _value: "张三",
                    get value() {
                        track()  // 订阅操作  => 所有依赖当前数据的其他数据, 都是当前数据的订阅者
                        return zhang.name
                    },
                }
            
            
            */
            /* const name = computed(function(){ // getter函数 => 对name取值时
                return  zhang.name;
            })
            const age = computed(function(){
                return  zhang.age;
            })

            console.log(name.value);
            console.log(age.value); */




            /*  
                可读可写
                 const nameRef = {
                    _value: "张三",
                    get value() {
                        track()  // 订阅操作  => 所有依赖当前数据的其他数据, 都是当前数据的订阅者
                        return zhang.name
                    },
                    set value(val){
                        trigger();
                        zhang.name = val;
                    }
                }
            */

            const name = computed({
                get:function(){ // getter函数 => 对name取值时
                    return  zhang.name;
                },
                set:function(val){  // setter函数 => 对name赋值,修改zhang.name
                    zhang.name = val;
                }
            })

            
            const age = computed({
                get:function(){ // getter函数 => 对age取值时
                    return  zhang.age;
                },
                set:function(val){  // setter函数 => 对age赋值,修改zhang.age
                    zhang.age = val;
                }
            })

            console.log(name.value);
            console.log(age.value);





            function addX() {
                obj.x++;
            }

            const addY = () => {
                obj.y++;
            }

            // !!!!!  报错!! reactive() 返回的Proxy代理对象 
            const reset = () => {
                obj = { x: 1, y: 2 };
            }


            const changeAge = () => {
                zhang.age++;
            }

            const changeChinese = () => {
                zhang.grade.chinese++;
            }

            // !!!!!!!!!  报错!! reactive() 返回的Proxy代理对象 
            const changeZhang = () => {
                zhang.value = {
                    name: "李四",
                    age: 22,
                    hobby: ["sing", "dance", "basketball"],
                    grade: {
                        chinese: 78,
                        math: 89,
                        english: 91,
                    }
                }
            }



            return {
                obj,
                addX: addX,
                addY,
                reset,

                zhang,
                changeAge,
                changeChinese,

                name,
                age,
            }

        },
        mounted() {
            console.log("mounted", this);

            /* 
                this => 当前vue实例的Proxy代理对象(模拟)
                Proxy => {   // 写法只是方便理解
                    get a(){
                        return a.value;
                    }
                    set a(val){
                        a.value = val;
                    }
                }

            
            
            */
        },
    })

    app.mount("#app");
</script>

</html>